package com.wjh.serivce.impl;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson2.JSON;
import com.wjh.common.LockConstant;
import com.wjh.enums.PayResultEnum;
import com.wjh.enums.PayStateEnum;
import com.wjh.enums.UpdateUserAmountEnum;
import com.wjh.mapper.IPayRecordMapper;
import com.wjh.order.request.UpdateOrderPayStateRequest;
import com.wjh.pay.entity.PayRecordEntity;
import com.wjh.serivce.ICommonService;
import com.wjh.serivce.IPayCallbackService;
import com.wjh.user.request.UpdateAmountRequest;

import lombok.extern.slf4j.Slf4j;

/**
 * 支付回调Service实现类
 * 
 * @author wenjianhai
 * @date 2023/6/29
 * @since JDK 17
 */
@Slf4j
@Service
public class PayCallbackServiceImpl implements IPayCallbackService {
	/** 支付记录Mapper */
	@Autowired
	private IPayRecordMapper payRecordMapper;

	@Autowired
	private ICommonService commonService;

	@Autowired
	private RedissonClient redissonClient;

	/**
	 * 订单支付成功处理
	 *
	 * @param payRecord 支付记录
	 * @param params    回调参数
	 * @param tip       功能名称
	 * @date 2023-06-29
	 * @author wenjianhai
	 * @since JDK 17
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void handlePaySuccess(PayRecordEntity payRecord, Map<String, String> params, String tip) {
		log.info(tip + ",订单支付成功处理-开始.支付记录:{}, 回调参数:{}", JSON.toJSONString(payRecord), JSON.toJSONString(params));
		// 支付记录Guid
		String recordGuid = payRecord.getRecordGuid();

		if (StringUtils.isBlank(recordGuid)) {
			log.error(tip + ",订单支付成功处理,支付记录Guid为空");
			throw new RuntimeException("支付记录Guid为空");
		}
		// 分布式锁
		RLock lock = null;

		try {
			String rdsKey = String.format(LockConstant.HANDLE_PAY_SUCCESS_LOCK_NAME, recordGuid);
			// 分布式锁
			lock = redissonClient.getLock(rdsKey);

			if (!lock.tryLock(LockConstant.HANDLE_PAY_SUCCESS_LOCK_WAIT_TIME,
					LockConstant.HANDLE_PAY_SUCCESS_LOCK_LEASE_TIME, TimeUnit.SECONDS)) {
				log.info(tip + ",订单支付成功处理,获取锁失败.rdsKey:{}", rdsKey);
				if (lock.isLocked()) {
					lock.unlock();
				}
				return;
			}
			// 查询支付结果（0:支付中, 1:支付成功, 2:支付失败, 3:支付超时）
			Integer payResult = payRecordMapper.queryPayResult(recordGuid);

			if (PayResultEnum.SUCCESS.getCode().equals(payResult)) {
				// 订单已支付成功（由于payOrder和callback都调用了此方法，所以加此判断是为了防止重复扣减用户可用金额）
				log.info(tip + ",订单支付成功处理,订单已支付成功.支付记录Guid:{}, 订单Guid:{}, 订单号:{}", recordGuid,
						payRecord.getOrderGuid(), payRecord.getOrderNo());
				return;
			}
			// 支付宝交易号
			String tradeNo = params.get("trade_no");

			// 支付记录
			payRecord.setThirdTradeNo(tradeNo);
			payRecord.setPayResult(PayResultEnum.SUCCESS.getCode());
			payRecord.setPayRemark("支付成功");
			// 更新支付记录
			payRecordMapper.updateForPay(payRecord);

			// 更新订单的支付状态
			this.updateOrderPayState(payRecord.getOrderGuid(), payRecord.getPayMode(), PayStateEnum.SUCCESS.getCode());

			// 扣除用户余额
			this.updateUserAmount(payRecord.getCreatorCode(), payRecord.getPayAmount(),
					UpdateUserAmountEnum.DEDUCT.getCode(), "订单支付回调");

			log.info(tip + ",订单支付成功处理-结束.支付记录:{}, 回调参数:{}", JSON.toJSONString(payRecord), JSON.toJSONString(params));
		} catch (Exception e) {
			log.error(String.format(tip + ",订单支付成功处理-开始.支付记录:%s, 回调参数:%s", JSON.toJSONString(payRecord),
					JSON.toJSONString(params)), e);
			throw new RuntimeException("订单支付成功处理失败");
		}
	}

	/**
	 * 更新订单的支付状态
	 *
	 * @date 2023-06-28
	 * @param orderGuid 订单Guid
	 * @param payMode   支付方式（1:支付宝, 2:微信, 3:通联）
	 * @param payState  支付状态（0:未支付, 1:支付中, 2:支付成功, 3:支付失败）
	 * @since JDK 17
	 * @author wenjianhai
	 */
	private void updateOrderPayState(String orderGuid, Integer payMode, Integer payState) {
		UpdateOrderPayStateRequest request = UpdateOrderPayStateRequest.builder().orderGuid(orderGuid).payMode(payMode)
				.payState(payState).build();
		commonService.updateOrderPayState(request, "订单支付");
	}

	/**
	 * 更新用户可用金额
	 *
	 * @date 2023-06-29
	 * @param userCode 用户编码
	 * @param amount   金额（单位：分）
	 * @param flag     标识（1:增加, 2:扣除）
	 * @param tip      功能名称
	 * @since JDK 17
	 * @author wenjianhai
	 */
	private void updateUserAmount(String userCode, Long amount, int flag, String tip) {
		UpdateAmountRequest request = UpdateAmountRequest.builder().userCode(userCode).amount(amount).flag(flag)
				.tip(tip).build();

		commonService.updateUserAmount(request);
	}
}
